package com.tcsa.openplatform.util;

import com.tcsa.openplatform.enums.AesAlgorithmForm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Base64Utils;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;

/**
 * 类名称：AESUtils
 * 类描述：AES加密工具
 * 创建时间：2020/8/13 14:21
 *
 * @author：shane
 * @Mail: shanbaohua@tcsa.cloud
 */
@Slf4j
public class AESUtil {

    private static final String CHAR_SET = "UTF-8";

    private static final String ALGORITHM = "AES";

    private static final int AES_KEY_SIZE = 128;

    /**
     * 加密（默认ECB）
     * 返回Base64 encode后的字符串
     * @param content  需要加密的内容
     * @param password 加密密码
     * @return
     */
    public static String encrypt2Str(String content, String password) throws UnsupportedEncodingException {
        byte[] encryptResult = encrypt(content, password);
        return Base64Utils.encodeToString(encryptResult);
    }

    /**
     * 解密（默认ECB）
     * 返回字符串
     * @param signedContent
     * @param password
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String decrypt2Str(String signedContent, String password) throws UnsupportedEncodingException {
        byte[] decryptResult = decrypt(Base64Utils.decodeFromString(signedContent), password);
        return new String(decryptResult, CHAR_SET);
    }

    /**
     * 加密（默认ECB）
     *
     * @param content  需要加密的内容
     * @param password 加密密码
     * @return
     */
    private static byte[] encrypt(String content, String password) throws UnsupportedEncodingException {
        byte[] byteContent = content.getBytes(CHAR_SET);
        return crypt(byteContent, password, Cipher.ENCRYPT_MODE);
    }


    /**
     * 解密（默认ECB）
     *
     * @param content  待解密内容
     * @param password 解密密钥
     * @return
     */
    private static byte[] decrypt(byte[] content, String password) {
        return crypt(content, password, Cipher.DECRYPT_MODE);
    }

    /**
     * 加/解密（默认ECB）
     *
     * @param content  待加/解密内容
     * @param password 加/解密密钥
     * @param mode 加/解密
     * @return
     */
    private static byte[] crypt(byte[] content, String password, int mode) {
        return crypt(content, password, mode, AesAlgorithmForm.ECB);
    }

    /**
     * 加/解密
     *
     * @param content  待加/解密内容
     * @param password 加/解密密钥
     * @param mode 加/解密
     * @param algorithm 算法形式
     * @return
     */
    private static byte[] crypt(byte[] content, String password, int mode, AesAlgorithmForm algorithm) {
        try {
            KeyGenerator kGen = KeyGenerator.getInstance(ALGORITHM);

            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(password.getBytes());

            kGen.init(AES_KEY_SIZE, secureRandom);
            SecretKey secretKey = kGen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, ALGORITHM);
            // 创建密码器
            Cipher cipher;
            if (AesAlgorithmForm.CBC == algorithm) {
                IvParameterSpec iv = new IvParameterSpec(secretKey.getEncoded());
                cipher = Cipher.getInstance(algorithm.getForm());
                // 初始化
                cipher.init(mode, key, iv);
            } else {
                cipher = Cipher.getInstance(algorithm.getForm());
                cipher.init(mode, key);
            }
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (Exception e) {
            log.error("AES decrypt error.", e);
        }
        return null;
    }

    public static void main(String[] args) throws UnsupportedEncodingException {
        String password = "Yxo#072zSzoN";
        String content = "{\"code\":0,\"msg\":\"SUCCEED\"}";
        //加密
        System.out.println("加密前：" + content);

        String encryptResult = encrypt2Str(content, password);

        System.out.println("加密后：" + encryptResult);
        System.out.println("加密后字节长度：" + encryptResult.length());

        //解密
        String decryptResult = decrypt2Str(encryptResult, password);
        System.out.println("解密后：" + decryptResult);
    }

}
